/*
 * $Id: MemoryWarningSystem.java 203 2009-06-18 15:21:14Z meyer@alphatec.de $
 * 
 *
 * Copyright 2009 DeltaDesk
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package deltadesk.apps.pictureviewer.util.management;

import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryNotificationInfo;
import java.lang.management.MemoryPoolMXBean;
import java.lang.management.MemoryType;
import java.util.LinkedList;
import java.util.List;

import javax.management.Notification;
import javax.management.NotificationEmitter;
import javax.management.NotificationListener;

/**
 * MemoryWarningSystem
 * 
 * @author Andreas Meyer
 * @version $Revision: 203 $
 */
public class MemoryWarningSystem {
	
	/** The heap pool. */
	private static final MemoryPoolMXBean heapPool = getHeapPool();

	/** The list of listeners. */
	private final List<Listener> listeners = new LinkedList<Listener>();

	/**
	 * Callback listener.
	 */
	public interface Listener {
	    
		public void memoryUsageLow(long usedMemory, long maxMemory);
	}

	
	/**
	 * Construct the warning system.
	 * 
	 */
	public MemoryWarningSystem() {
		
		final MemoryMXBean mbean = ManagementFactory.getMemoryMXBean();
	    final NotificationEmitter emitter = (NotificationEmitter) mbean;
	    emitter.addNotificationListener(new NotificationListener() {

			public void handleNotification(final Notification n,
					                       final Object handback) {
				
				if (n.getType().equals(
						MemoryNotificationInfo.MEMORY_THRESHOLD_EXCEEDED)) {

			          long maxMemory = heapPool.getUsage().getMax();
			          long usedMemory = heapPool.getUsage().getUsed();
			          
			          for (Listener listener : listeners) {
			            listener.memoryUsageLow(usedMemory, maxMemory);
			          } // end for
				} // end if
			}
	    	
	    }, null, null);
	}
	
	/**
	 * Add a listener.
	 * 
	 * @param listener
	 * @return
	 */
	public boolean addListener(final Listener listener) {
		return listeners.add(listener);
	}

	/**
	 * Remove a listener.
	 * 
	 * @param listener
	 * @return
	 */
	public boolean removeListener(final Listener listener) {
		return listeners.remove(listener);
	}

	/**
	 * Set the usage threshold as percentage value.
	 * Example: If the listener should be notified if the memory usage is
	 *          more than 90%, then set the value to 0.9
	 * 
	 * @param percentage
	 */
	public static void setPercentageUsageThreshold(final double percentage) {

		if (percentage <= 0.0 || percentage > 1.0) {
			throw new IllegalArgumentException("Percentage not in range");
		}

		long maxMemory = heapPool.getUsage().getMax();
		long warningThreshold = (long) (maxMemory * percentage);
		heapPool.setUsageThreshold(warningThreshold);
	}
	
	/**
	 * Set the usage threshold as minimum memory value.
	 * 
	 * @param minMemory
	 */
	public static void setMinMemoryThreshold(final long minMemory) {
		
		long maxMemory = heapPool.getUsage().getMax();
		long warningThreshold = (long) (maxMemory - minMemory);
		
		if(warningThreshold < 0) {
			throw new AssertionError(
					"MemoryWarningSystem: Available memory smaller than warning level");
		} // end if
		
		heapPool.setUsageThreshold(warningThreshold);
	}

	/**
	 * Return the memory pool for heap space.
	 * 
	 * @return
	 */
	private static MemoryPoolMXBean getHeapPool() {

		final List<MemoryPoolMXBean> pools = ManagementFactory.getMemoryPoolMXBeans();
		for (final MemoryPoolMXBean pool : pools) {

			if ((pool.getType() == MemoryType.HEAP) &&
				 pool.isUsageThresholdSupported()) {

				return pool;
			} // end if
		} // end for

		throw new AssertionError("MemoryWarningSystem: Could not find memory pool");
	}
}
